#include <list>
#include <cstdio>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "projection.h"
#include "minIni.h"
#include "config.h"
#include "resources.h"
#include "spriteanimator.h"
#include "inputhandler.h"
#include "sprite.h"
#include "blitqueue.h"
#include "grcontext.h"
#include "map.h"
#include "entityaction.h"
#include "entity.h"
#include "entitymanager.h"

using namespace Oribi;

GraphicContext::GraphicContext():
    screenWidth(DEFAULT_SCREEN_WIDTH),
    screenHeight(DEFAULT_SCREEN_HEIGHT),
    window(0),
    display(0),
    cameraSpeed(1),
    cameraPosition{-300, -100}{}

#define WINDOW_FLAGS SDL_WINDOW_SHOWN | SDL_WINDOW_FULLSCREEN

int GraphicContext::initialise(ConfContext& conf)
{
    if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
        return 1;

    int width  = conf.geti("conf/confmain.ini", "WindowSize", "width",  -1);
    int height = conf.geti("conf/confmain.ini", "WindowSize", "height", -1);

    if (width > 0)
        screenWidth = width;
    if (height > 0)
        screenHeight = height;

    window = SDL_CreateWindow("Oribi",
                              SDL_WINDOWPOS_UNDEFINED,
                              SDL_WINDOWPOS_UNDEFINED,
                              screenWidth, screenHeight,
                              WINDOW_FLAGS);
    if (!window)
        return 2;

    display = SDL_GetWindowSurface(window);
    if (!display)
        return 3;

    if (IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF)
              != (IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF))
        return 4;

    return 0;
}

void GraphicContext::shutDown()
{
    if (!window)
        return;

    SDL_DestroyWindow(window);
    IMG_Quit();
    SDL_Quit();
}

SDL_Surface *GraphicContext::getDisplaySurface()
{
    if (!window)
        return NULL;
    return display;
}

void GraphicContext::beginFrame()
{
    frameBeginTime = SDL_GetTicks();
    SDL_FillRect(display, NULL, SDL_MapRGB(display->format, 0, 0, 0));

    blitQueue.clear();
    blitQueue.globalOffset = {-cameraPosition.x, -cameraPosition.y};
    for (auto it = sprites.begin(); it != sprites.end(); it++)
    {
        if (*it)
            blitQueue.queueSprite(**it);
    }
}

void GraphicContext::addSpriteToBlitQueue(const Sprite& spr)
{
    blitQueue.queueSprite(spr);
}

void GraphicContext::blitSprites()
{
    blitQueue.blitAll(display);
}

void GraphicContext::endFrame()
{
    SDL_UpdateWindowSurface(window);
}

void GraphicContext::addSprite(Sprite *spr)
{
    if (!spr)
        return;

    for (auto it = sprites.begin(); it != sprites.end(); ++it)
    {
        if ((*it) == spr)
            return;
    }

    sprites.push_front(spr);
}

#define SCROLLING_MARGIN 5

void GraphicContext::updateCamera(const MouseState& mouse)
{
    SDL_Point v = mouse.position;
    if (v.x > screenWidth - SCROLLING_MARGIN)
        v.x = 1;
    else if (v.x < SCROLLING_MARGIN)
        v.x = -1;
    else
        v.x = 0;

    if (v.y > screenHeight - SCROLLING_MARGIN)
        v.y = 1;
    else if (v.y < SCROLLING_MARGIN)
        v.y = -1;
    else
        v.y = 0;

    moveCamera(v);
}

void GraphicContext::moveCamera(SDL_Point p)
{
    if (p.x > 0)
        p.x = 1;
    if (p.x < 0)
        p.x = -1;
    if (p.y > 0)
        p.y = 1;
    if (p.y < 0)
        p.y = -1;

    Uint32 t = SDL_GetTicks() - frameBeginTime;
    cameraPosition.x += p.x * cameraSpeed * t;
    cameraPosition.y += p.y * cameraSpeed * t;
}

bool GraphicContext::rectIntersectsFOV(const SDL_Rect& rect) const
{
    SDL_Rect camRect = getCameraRect();
    return (camRect.x <= rect.x + rect.w
         && camRect.x + camRect.w >= rect.x
         && camRect.y <= rect.y + rect.h
         && camRect.y + camRect.h >= rect.y);
}

SDL_Point GraphicContext::getCameraPosition() const
{
    return cameraPosition;
}

SDL_Rect GraphicContext::getCameraRect() const
{
    return {cameraPosition.x, cameraPosition.y, screenWidth, screenHeight};
}
